﻿Imports Library
Imports System.Collections.ObjectModel

Public Class StockInfoChartControl

#Region "property"

    ''' <summary>
    ''' set which stock id could be use in this control
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property StockID As String
        Get
            Return m_StockID
        End Get
        Set(ByVal value As String)
            If value Is Nothing Then Exit Property
            If value ="" then Exit Property

            m_StockID = value
            m_StockName = GetStockNameByID(m_StockID)

            If m_StockData Is Nothing Then Exit Property
            'if stock data already set , then sort stock data by stock id
            If m_StockData.Count > 0 Then
                For Each StockInfoTmp As clsMyStockInfo In m_StockData
                    If StockInfoTmp.StockID <> m_StockID Then
                        m_StockData.Remove(StockInfoTmp) '???test if this statement can delete all 
                    End If
                Next
                m_Alert.MyStocks = m_StockData 'reset alert data
                UpdateDisplay()
            End If
        End Set
    End Property

    ''' <summary>
    ''' set what to be display in series title
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property DisplaySeriesTitle As eDisplaySeriesTitle
        Get
            Return m_DisplaySeriesTitle
        End Get
        Set(ByVal value As eDisplaySeriesTitle)
            m_DisplaySeriesTitle = value
        End Set
    End Property

    ''' <summary>
    ''' if display profit percentage in each user hold stock
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property DisplayProfit As Boolean
        Get
            Return m_bDisplayProfit
        End Get
        Set(value As Boolean)
            m_bDisplayProfit = value
            UpdateDisplay() 'force update display
        End Set
    End Property

#End Region

#Region "Method"

    Public Sub SetCurrentStockInfo(ByVal value As clsStockInfo)
        m_StockInfo = value

        'update all user set stocks' profit
        For Each StockInfoTmp As clsMyStockInfo In m_StockData
            If StockInfoTmp.StockID = m_StockInfo.StockID Then
                StockInfoTmp.UpdateProfit(m_StockInfo.CurrentPrice)
            End If
        Next

        If m_Alert.SetCurrentPrice(value.CurrentPrice) Then 'set alert current state
            'alert on
            m_Alerting = True
            RaiseEvent Alert(Me)
        End If

        UpdateDisplay()
    End Sub

    ''' <summary>
    ''' set user stock data
    ''' </summary>
    ''' <param name="value"></param>
    ''' <remarks></remarks>
    Public Sub SetMyStockData(ByVal value As Collection(Of clsMyStockInfo))
        If value Is Nothing Then Exit Sub

        If m_StockID = "" Then
            'just load all data but don't update display
            m_StockData = value

        Else
            m_StockData.Clear()
            'only add same stock id
            For Each StockInfoTmp As clsMyStockInfo In value
                If StockInfoTmp.StockID = m_StockID Then
                    m_StockData.Add(StockInfoTmp)
                End If
            Next
            m_Alert.MyStocks = m_StockData 'reset alert data
            UpdateDisplay()
        End If
    End Sub

    ''' <summary>
    ''' set alert object 
    ''' </summary>
    ''' <param name="Alert"></param>
    ''' <remarks></remarks>
    Public Sub SetAlert(ByVal Alert As clsAlert)
        If Alert Is Nothing Then Exit Sub

        m_Alert = Alert
    End Sub
#End Region

#Region "event"
    Public Event Alert(ByVal Sender As Object)
#End Region

    Public Enum eDisplaySeriesTitle
        NoDisplay = 0
        StockID = 1
        StockName = 2

    End Enum

    Private Const SPACE As Integer = 30 'space between Axis to control border

    Private m_StockData As Collection(Of clsMyStockInfo) 'user's stock data
    Private m_StockID As String 'only display one stock in one control
    Private m_StockName As String
    Private m_Scale As Double  'drawing scale between price and length
    Private m_StockInfo As clsStockInfo 'current stock infor
    Private m_MaxPrice As Double 'max price to display on axis
    Private m_MinPrice As Double 'min price to display on axis
    Private m_DisplaySeriesTitle As eDisplaySeriesTitle 'what to display below axis x as series title
    Private m_PriceIcons As Collection(Of clsPriceIcon) 'collection of all price icon
    Private m_Alert As clsAlert 'alert object
    Private m_Alerting As Boolean 'if alerting


    'display option
    Private m_bDisplayProfit As Boolean 'if display profit


    Sub New()
        ' This call is required by the designer.
        InitializeComponent()
        'Me.MinimumSize = New Size(SPACE, SPACE * 4)
        m_StockData = New Collection(Of clsMyStockInfo)
        m_StockID = ""
        m_StockName = ""
        m_Scale = 1
        m_MaxPrice = 0
        m_MinPrice = 0
        m_DisplaySeriesTitle = eDisplaySeriesTitle.StockName
        Me.MinimumSize = New Size(SPACE * 3, SPACE * 4)
        m_StockInfo = New clsStockInfo
        m_PriceIcons = New Collection(Of clsPriceIcon)
        m_Alert = New clsAlert
        m_Alerting = False

    End Sub

    Private Sub UpdateDisplay()
        UpdateAxis()
        UpdateDataPoints()

    End Sub


    ''' <summary>
    ''' draw coordinate according max value and min value
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub UpdateAxis()
        If m_StockData Is Nothing Then Exit Sub
        If m_StockInfo Is Nothing Then Exit Sub

        'set max value and min value of y axis
        Dim i As Integer
        If m_StockData.Count = 0 Then
            Exit Sub
        End If

        m_MaxPrice = m_StockData(0).CostPrice
        m_MinPrice = m_StockData(0).CostPrice

        'get max and min price
        For i = 0 To m_StockData.Count - 1
            'compare user stored price
            If m_StockData(i).CostPrice > m_MaxPrice Then
                m_MaxPrice = m_StockData(i).CostPrice
            End If

            If m_StockData(i).CostPrice < m_MinPrice Then
                m_MinPrice = m_StockData(i).CostPrice
            End If
        Next
        'compare current price
        If m_StockInfo.CurrentPrice < m_MinPrice Then
            m_MinPrice = m_StockInfo.CurrentPrice
        End If
        If m_StockInfo.CurrentPrice > m_MaxPrice Then
            m_MaxPrice = m_StockInfo.CurrentPrice
        End If

        'compare today's max min price
        If m_StockInfo.TodayMin < m_MinPrice Then
            m_MinPrice = m_StockInfo.TodayMin
        End If
        If m_StockInfo.TodayMax > m_MaxPrice Then
            m_MaxPrice = m_StockInfo.TodayMax
        End If


        'draw axis
        Dim img As New Bitmap(Me.Width, Me.Height)
        Dim g As Graphics = Graphics.FromImage(img)
        Dim AxisPen As New Pen(Me.ForeColor)
        Dim AxisStringBrush As New SolidBrush(Me.ForeColor)

        'draw x axis
        g.DrawLine(AxisPen, SPACE, Me.Height - SPACE, Me.Width - SPACE, Me.Height - SPACE)

        'draw y axis
        g.DrawLine(AxisPen, SPACE, Me.Height - SPACE, SPACE, SPACE)
        Dim GridLines As Integer = (Me.Height - 2 * SPACE) \ SPACE + 1 'grid line number
        Dim StepLength As Integer  'step length of each mark
        Dim j As Integer = 0

        If (m_MaxPrice - m_MinPrice) * 100 + 1 < GridLines Then 'price interval less then grid interval
            StepLength = ((Me.Height - 2 * SPACE) \ ((m_MaxPrice - m_MinPrice) * 100 + 1))
        Else
            StepLength = SPACE
        End If

        m_Scale = (m_MaxPrice - m_MinPrice) / (Me.Height - 2 * SPACE - StepLength)

        For i = SPACE + StepLength To (Me.Height - SPACE) - StepLength Step StepLength
            g.DrawLine(AxisPen, SPACE, i, SPACE + SPACE \ 5, i)
            g.DrawString(Format((m_MaxPrice - StepLength * j * m_Scale), "0.00"), Me.Font, AxisStringBrush, 0, i - g.MeasureString(Format((m_MaxPrice - StepLength * j * m_Scale), "0.00"), Me.Font).Height \ 2)
            j = j + 1
        Next
        g.DrawLine(AxisPen, SPACE, Me.Height - SPACE, SPACE + SPACE \ 5, Me.Height - SPACE)
        g.DrawString(Format(m_MinPrice, "0.00"), Me.Font, AxisStringBrush, 0, Me.Height - SPACE - g.MeasureString(Format(m_MinPrice, "0.00"), Me.Font).Height \ 2)

        'draw axis y array
        g.DrawLine(AxisPen, SPACE, SPACE, SPACE - SPACE \ 10, SPACE + SPACE \ 5)
        g.DrawLine(AxisPen, SPACE, SPACE, SPACE + SPACE \ 10, SPACE + SPACE \ 5)

        'draw stock name
        Select Case m_DisplaySeriesTitle
            Case eDisplaySeriesTitle.StockID
                g.DrawString(m_StockID, Me.Font, AxisStringBrush, (Me.Width - g.MeasureString(m_StockName, Me.Font).Width) \ 2, Me.Height - SPACE \ 2 - g.MeasureString(m_StockID, Me.Font).Height \ 2)
            Case eDisplaySeriesTitle.StockName
                g.DrawString(m_StockName, Me.Font, AxisStringBrush, (Me.Width - g.MeasureString(m_StockName, Me.Font).Width) \ 2, Me.Height - SPACE \ 2 - g.MeasureString(m_StockName, Me.Font).Height \ 2)
            Case Else

        End Select

        Me.BackgroundImage = img
    End Sub

    ''' <summary>
    ''' draw data points
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub UpdateDataPoints()
        If m_StockData Is Nothing Then Exit Sub
        If m_StockData.Count = 0 Then Exit Sub

        Dim img As New Bitmap(Me.Width, Me.Height)
        Dim g As Graphics = Graphics.FromImage(img)
        Dim PointsPen As New Pen(Me.ForeColor)
        Dim MinMaxPen As New Pen(Color.Red)
        Dim PointsStringBrush As New SolidBrush(Me.ForeColor)
        Dim PricePoint As clsPriceIcon
        Dim x As Integer = Me.Width \ 2
        Dim y As Integer

        m_PriceIcons.Clear()

        'draw user set stock data
        For Each StockDataTmp As clsMyStockInfo In m_StockData
            PricePoint = New clsPriceIcon
            PricePoint.PointColor = Color.Blue
            y = CInt(Me.Height - SPACE - (StockDataTmp.CostPrice - m_MinPrice) / m_Scale)
            PricePoint.CenterPoin = New Point(x, y)
            PricePoint.DrawLine(g)

            If m_bDisplayProfit Then
                PricePoint.DrawProfit(g, Me.Font, StockDataTmp.ProfitPercentage) 'draw percentage of profit
            End If

            m_PriceIcons.Add(PricePoint)
        Next

        'draw current stock data
        PricePoint = New clsPriceIcon
        PricePoint.PointColor = Color.Salmon
        y = CInt(Me.Height - SPACE - (m_StockInfo.CurrentPrice - m_MinPrice) / m_Scale)
        PricePoint.CenterPoin = New Point(x, y)
        PricePoint.DrawTriangleRight(g)
        m_PriceIcons.Add(PricePoint)


        'draw today's max price and min price
        PricePoint = New clsPriceIcon
        PricePoint.PointColor = Color.Red
        y = CInt(Me.Height - SPACE - (m_StockInfo.TodayMax - m_MinPrice) / m_Scale)
        PricePoint.CenterPoin = New Point(SPACE, y)
        PricePoint.DrawTriangleDown(g)
        m_PriceIcons.Add(PricePoint)


        PricePoint = New clsPriceIcon
        PricePoint.PointColor = Color.Red
        y = CInt(Me.Height - SPACE - (m_StockInfo.TodayMin - m_MinPrice) / m_Scale)
        PricePoint.CenterPoin = New Point(SPACE, y)
        PricePoint.DrawTriangleUp(g)
        m_PriceIcons.Add(PricePoint)



        picData.Image = img

    End Sub

    Private Sub StockInfoChart_Resize(sender As System.Object, e As System.EventArgs) Handles MyBase.Resize
        UpdateDisplay()
    End Sub






    Private Class clsPriceIcon

#Region "property"
        ''' <summary>
        ''' get or set the center coordinate of price icon
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property CenterPoin As Point
            Get
                Return m_CenterPoint
            End Get
            Set(value As Point)
                m_CenterPoint = value
            End Set
        End Property

        ''' <summary>
        ''' set or get color of this point
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property PointColor As Color
            Get
                Return m_ForeColor
            End Get
            Set(value As Color)
                m_ForeColor = value
                m_PointPen = New Pen(m_ForeColor)

            End Set
        End Property


#End Region

#Region "method"
        ''' <summary>
        ''' draw this price icon
        ''' </summary>
        ''' <remarks></remarks>
        Public Sub DrawLine(ByRef g As Graphics)
            g.DrawLine(m_PointPen, m_CenterPoint.X, m_CenterPoint.Y, m_CenterPoint.X + SPACE \ 3, m_CenterPoint.Y)
        End Sub

        Public Sub DrawTriangleDown(ByRef g As Graphics)
            Dim p(2) As Point
            p(0) = New Point(m_CenterPoint.X, m_CenterPoint.Y)
            p(1) = New Point(m_CenterPoint.X - SPACE \ 10, m_CenterPoint.Y - SPACE \ 10)
            p(2) = New Point(m_CenterPoint.X + SPACE \ 10, m_CenterPoint.Y - SPACE \ 10)

            g.FillPolygon(New SolidBrush(m_ForeColor), p)
        End Sub

        Public Sub DrawTriangleUp(ByRef g As Graphics)
            Dim p(2) As Point
            p(0) = New Point(m_CenterPoint.X, m_CenterPoint.Y)
            p(1) = New Point(m_CenterPoint.X - SPACE \ 10, m_CenterPoint.Y + SPACE \ 10)
            p(2) = New Point(m_CenterPoint.X + SPACE \ 10, m_CenterPoint.Y + SPACE \ 10)

            g.FillPolygon(New SolidBrush(m_ForeColor), p)
        End Sub

        Public Sub DrawTriangleRight(ByRef g As Graphics)
            Dim p(2) As Point
            p(0) = New Point(m_CenterPoint.X, m_CenterPoint.Y)
            p(1) = New Point(m_CenterPoint.X - SPACE \ 10, m_CenterPoint.Y + SPACE \ 10)
            p(2) = New Point(m_CenterPoint.X - SPACE \ 10, m_CenterPoint.Y - SPACE \ 10)

            g.FillPolygon(New SolidBrush(m_ForeColor), p)
        End Sub

        Public Sub DrawProfit(ByRef g As Graphics, ByVal font As Font, ByVal strText As String)
            If strText = "" Then Exit Sub
            If strText.Substring(0, 1) <> "-" Then
                g.DrawString(strText, font, New SolidBrush(Color.Red), m_CenterPoint.X + SPACE \ 3, m_CenterPoint.Y)
            Else
                g.DrawString(strText, font, New SolidBrush(Color.Green), m_CenterPoint.X + SPACE \ 3, m_CenterPoint.Y)
            End If

        End Sub

#End Region


        Private m_CenterPoint As Point 'price point coordinate
        Private m_ForeColor As Color
        Private m_PointPen As Pen

        Sub New()
            m_CenterPoint = New Point(0, 0)
            m_ForeColor = Color.Black
            m_PointPen = Pens.Black
        End Sub
    End Class


End Class

